home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 …ember: Reference Library / Dev.CD Dec 00 RL Disk 1.toast / pc / technical documentation / develop / develop issue 26 / develop issue 26 code / truffles - display mgr. / sprocket / experimentalstuff / exceptions.cp < prev    next >
Encoding:
Text File  |  1995-12-10  |  7.7 KB  |  304 lines

  1. /*============================================================
  2.     Exceptions.c
  3.     
  4.     greggor@apple.com
  5.     
  6.     New API matches C++ exceptions as much as possible
  7.     
  8.     Usage:
  9.     
  10.     #include "Exceptions.h"
  11.     
  12.         Ptr ptr1 = nil;
  13.         Ptr ptr2 = nil;
  14.         OSErr err = noErr;
  15.         
  16.         NOREGISTER(ptr1);
  17.         NOREGISTER(ptr2);
  18.         
  19.         Try
  20.         {
  21.             ptr1 = NewPtr( 1024 * 1024 );
  22.             FailErr(MemError());
  23.             ptr2 = NewPtr( 15 );
  24.             FailErr(MemError());
  25.             
  26.             if(CatastrophicError())
  27.                 Throw(eCatastrophicError);
  28.         }
  29.         Catch(err)
  30.         {
  31.             //
  32.             //    Clean up here
  33.             //
  34.             if( ptr1 != nil )
  35.                 DisposPtr( ptr1 );
  36.             if( ptr2 != nil )
  37.                 DisposPtr( ptr2 );
  38.             
  39.             if(err == eCatastrophicError)
  40.                 Alert(blah blah blah);
  41.             
  42.             //
  43.             // Fail again...
  44.             //
  45.             Throw(err);
  46.         }
  47.  
  48. ============================================================*/
  49. #include <Types.h>
  50. #include <Memory.h>
  51. #include <Resources.h>
  52. #include <setjmp.h>
  53.  
  54. #include "Exceptions.h"
  55.  
  56. #define DEBUGMESSAGES 1
  57.  
  58. // #define FAILMESSAGES
  59.  
  60. NotifyFailureProc    gNotifyProc = nil;
  61.  
  62. #ifndef REAL_EXCEPTIONS
  63. TException*            gExceptionStack = nil;
  64.  
  65. //--------------------------------------------------------------------------------
  66. // TException::Setup
  67. //
  68. // This function is called by the TRY macro
  69. //--------------------------------------------------------------------------------
  70. Boolean TException::Setup()
  71. {
  72.     Boolean firstTime = false;
  73.     
  74.     if(this->fMagicFailID == kMagicFailID)
  75.     {
  76.         if((this->fFlags & kExceptionThrown) == 0)
  77.         {
  78. #ifdef DEBUGMESSAGES
  79.             if((this->fFlags & kTryBlockHasBeenSetup) != 0)
  80.                 DebugStr("\pTry setup reentered for initialization in a bogus way");
  81. #endif
  82.                         
  83.             //
  84.             // Clear the 'is resignaling' bit of the
  85.             // failure stack
  86.             //
  87.             if(gExceptionStack != nil)
  88.                 gExceptionStack->fFlags &= ~kResignalingFailure;
  89.             
  90.             //
  91.             // Push the new entry onto the failure stack
  92.             //
  93.             this->fNext = gExceptionStack;
  94.             gExceptionStack = this;
  95.             
  96.             this->fFlags |= (kTryFrameOnFailureStack | kTryBlockHasBeenSetup);
  97.             firstTime = true;
  98.         }
  99.         else
  100.         {
  101. #ifdef DEBUGMESSAGES
  102.             if((this->fFlags & kTryBlockHasBeenSetup) == 0)
  103.                 DebugStr("\pTry setup reentered for cleanup in a bogus way");
  104. #endif
  105.             
  106.             this->TearDown();
  107.         }
  108.     }
  109. #ifdef DEBUGMESSAGES
  110.     else
  111.         DebugStr("\pCorrupt fail info in Setup");
  112. #endif
  113.  
  114.     return firstTime;
  115. } // TException::Setup
  116.  
  117. //--------------------------------------------------------------------------------
  118. // TException::BeginTry
  119. //
  120. // This function is called by the TRY macro
  121. //--------------------------------------------------------------------------------
  122. Boolean TException::BeginTry()
  123. {
  124.     Boolean firstTime = false;
  125.     
  126.     if(this->fMagicFailID == kMagicFailID)
  127.     {
  128.         if((this->fFlags & kTryInProgress) == 0)
  129.         {
  130.             this->fFlags |= kTryInProgress;
  131.             firstTime = true;
  132.         }
  133.         else
  134.         {
  135.             this->fFlags |= kTrySucceeded;
  136.             this->TearDown();
  137.         }
  138.     }
  139. #ifdef DEBUGMESSAGES
  140.     else
  141.         DebugStr("\pCorrupt fail info in Setup");
  142. #endif
  143.     
  144.     return firstTime;
  145. } // TException::BeginTry
  146.  
  147. //--------------------------------------------------------------------------------
  148. // TException::TearDown
  149. //
  150. // 'TearDown' is called right before the exception handling block
  151. // is called (in which case fFlags & kExceptionThrown will be set), or after
  152. // a try block has completed successfully (in which case fFlags & kTrySucceeded
  153. // will be set).
  154. //--------------------------------------------------------------------------------
  155. void TException::TearDown()
  156. {
  157.     if((gExceptionStack == this) && (this->fMagicFailID == kMagicFailID) && ((this->fFlags & kTryFrameOnFailureStack) != 0))
  158.     {
  159.         //
  160.         // Pop an entry off the top of the exception stack
  161.         //
  162.         gExceptionStack = gExceptionStack->fNext;
  163.         this->fFlags &= ~kTryFrameOnFailureStack;
  164.         
  165.         //
  166.         // If we failed & there is anything left on the stack,
  167.         // then note that we might want to resignal
  168.         //
  169.         if((gExceptionStack != nil) && ((this->fFlags & kExceptionThrown) != 0))
  170.         {
  171.             gExceptionStack->fFlags |= kResignalingFailure;
  172.             gExceptionStack->fError = this->fError;
  173.         }
  174.     }
  175. #ifdef DEBUGMESSAGES
  176.     else
  177.     {
  178.         if(gExceptionStack == nil)
  179.             DebugStr( "\pError -- failure stack inexplicably nil" );
  180.         else
  181.             DebugStr( "\pTearDownTry:  Failure handler stack corrupted" );
  182.     }
  183. #endif
  184. } // TException::TearDown
  185.  
  186. //--------------------------------------------------------------------------------
  187. // Throw
  188. //
  189. // Invoke exception handling
  190. //--------------------------------------------------------------------------------
  191. void Throw(OSErr theErr)
  192. {
  193.     //
  194.     // First do a little bit of sanity checking
  195.     //
  196.     if((gExceptionStack != nil) && (gExceptionStack->fMagicFailID == kMagicFailID))
  197.     {
  198.         //
  199.         // Don't allow Throw(noErr)
  200.         //
  201.         if(theErr == noErr)
  202.             theErr = eGeneralErr;
  203.         
  204.         //
  205.         // Notify that we failed, unless we are resignalling
  206.         // Note:  If 'kResignalingFailure' is set, then fError
  207.         // will be the error we set last time.  Optionally, we
  208.         // could require that 'theErr == gExceptionStack->fError'
  209.         // in order to do notification, but we didn't.
  210.         //
  211.         if((gExceptionStack->fFlags & kResignalingFailure) == 0)
  212.             NotifyFailure(theErr);
  213.         
  214.         //
  215.         // Remember the error that caused the failure
  216.         //
  217.         gExceptionStack->fError = theErr;
  218.         
  219.         //
  220.         // TRY does a setjmp; now we longjmp back to
  221.         // that point with a longjmp.  The 'TRY' macro
  222.         // includes an 'if' statement that is true
  223.         // the first time through and false when we longjmp
  224.         // back to it; the EXCEPTion handler is just the
  225.         // 'else' branch of that 'if' block.
  226.         //
  227.         // Note:    This code used to use the result passed
  228.         //            back from longjmp, but that didn't work
  229.         //            under MetroWerks.  My guess is that MetroWerks
  230.         //            was confused about the size of an int,
  231.         //            probably due to the libraries I chose to
  232.         //            link with.  Rather than work out the problem,
  233.         //            I decided to stop using the return value
  234.         //            from longjmp.
  235.         //
  236.         gExceptionStack->fFlags |= kExceptionThrown;
  237.         longjmp(gExceptionStack->fEnv, kExceptionThrown);
  238.     }
  239. #ifdef DEBUGMESSAGES
  240.     else
  241.     {
  242.         if(gExceptionStack == nil)
  243.             DebugStr("\pthrow without try");
  244.         else
  245.             DebugStr( "\pthrow:  Failure handler stack corrupted" );
  246.     }
  247. #endif
  248. } // Throw
  249.  
  250. #endif    // REAL_EXCEPTIONS
  251.  
  252. //--------------------------------------------------------------------------------
  253. // SetExceptionNotifyProc
  254. //
  255. // Install a single routine called at failure time before
  256. // exception processing begins.  This function is used in
  257. // conjunction with ShowStackChain to record and later show
  258. // the stack at the time of failure (left over from old 68K
  259. // code that doesn't work too well on PowerPC).
  260. //--------------------------------------------------------------------------------
  261. void SetExceptionNotifyProc(NotifyFailureProc notifyProc)
  262. {
  263.     gNotifyProc = notifyProc;
  264. } // SetExceptionNotifyProc
  265.  
  266. //--------------------------------------------------------------------------------
  267. // ExceptionNotifyProcInstalled
  268. //
  269. // This routine is used by ReportError so that it knows whether or
  270. // not it should show the 'sc6' button
  271. //--------------------------------------------------------------------------------
  272. Boolean ExceptionNotifyProcInstalled(void)
  273. {
  274.     return gNotifyProc != nil;
  275. } // ExceptionNotifyProcInstalled
  276.  
  277. //--------------------------------------------------------------------------------
  278. // NotifyFailure
  279. //
  280. // Notify that an error occured
  281. //--------------------------------------------------------------------------------
  282. void NotifyFailure(OSErr err)
  283. {
  284. #ifdef FAILMESSAGES
  285.     DebugPrintf("Failure caused by an error %d", err);
  286. #endif
  287.  
  288.     // DebugStr("\pSomeone is about to fail...");
  289.  
  290.     if(gNotifyProc != nil)
  291.         (*gNotifyProc)(err);
  292. } // NotifyFailure
  293.  
  294. //--------------------------------------------------------------------------------
  295. // MakeVariableNoRegister
  296. //
  297. // This routine doesn't actually do anything; it's just used
  298. // by the NOREGISTER macro
  299. //--------------------------------------------------------------------------------
  300. void MakeVariableNoRegister( void* /* foo */ )
  301. {
  302.  
  303. } // MakeVariableNoRegister
  304.